﻿using System;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;

namespace produccion
{
        /// This class acts as the base class for any data entity which provides notifications whenever any of its property is changed.
        /// </summary>
        public abstract class ObservableObject : INotifyPropertyChanged
        {
            #region Data & Constructors
            private const Int32 notificationDisabled = 0;
            private const Int32 notificationEnabled = 1;

            private const String Error_Msg = "{0} is not a public property of {1}";
            private const String Error_SuspendNotification = "Nested SuspendNotification is not supported";
            private const String Error_ResumeNotification = "ResumeNotification without first calling SuspendNotification is not supported";
            private Int32 objectState = notificationEnabled;

            protected ObservableObject()
            {
            }

            #endregion

            #region Public Members

            /// <summary>
            /// Occurs when a property value changes.
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;

            /// <summary>
            /// Suspend the property changed notification.
            /// </summary>
            /// <remarks>
            /// After this call, the property changed notification is disabled.
            /// </remarks>
            public void SuspendNotification()
            {
                if (Interlocked.CompareExchange(ref objectState, notificationDisabled, notificationEnabled) != notificationEnabled)
                {
                    throw new InvalidOperationException(Error_SuspendNotification);
                }
            }

            /// <summary>
            /// Resume the property changed notification.
            /// </summary>
            /// <remarks>
            /// After this call, the property changed notification is re-enabled.
            /// </remarks>
            public void ResumeNotification()
            {
                if (Interlocked.CompareExchange(ref objectState, notificationEnabled, notificationDisabled) != notificationDisabled)
                {
                    throw new InvalidOperationException(Error_SuspendNotification);
                }
            }

            public Boolean IsNotificationEnabled
            {
                get { return Thread.VolatileRead(ref objectState) == 1; }
            }

            #endregion

            #region Protected Members

            ///<summary>
            /// Invoked whenever the value of any property is changed.
            ///</summary>
            ///<remarks>
            /// Derived classes can override this method to include any logic after the property is changed.
            ///</remarks>
            /// <param name="propertyName">
            /// The name of the property which was changed.
            /// </param>
            protected virtual void OnPropertyChanged(String propertyName)
            {
                // Do nothing
            }

            ///<summary>
            ///Raise the property changed event.
            ///</summary>
            /// <param name="propertyName">
            /// The name of the property which was changed.
            /// </param>
            protected void RaisePropertyChangedEvent(String propertyName)
            {
                VerifyProperty(propertyName);
                if (Thread.VolatileRead(ref objectState) == notificationDisabled)
                {
                    return;
                }

                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    // Raise the PropertyChanged event.
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }

                OnPropertyChanged(propertyName);
            }
            #endregion

            #region Private Helpers

            [Conditional("DEBUG")]
            private void VerifyProperty(String propertyName)
            {
                Type type = GetType();
                PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);

                Debug.Assert(propertyInfo != null, String.Format(Error_Msg, propertyName, type.FullName));
            }

            #endregion
        }
   }
